﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Ornament.Domain;

namespace Products.Databases
{
    [Serializable]
    public abstract class DatabaseObject : DomainObject<Guid>
    {
        private ISnapShot _currentSnapshot;
        private IList<ISnapShot> _snapshots;
        private ProductVersion _switchProductVersion;

        protected DatabaseObject()
        {
        }
        /// <summary>
        /// Create new DatabaseObject
        /// </summary>
        /// <param name="productVersion"></param>
        protected DatabaseObject(ProductVersion productVersion)
        {
            if (productVersion == null) 
                throw new ArgumentNullException("productVersion");

            CurrentSnapshot = CreateNewStateChange(productVersion, ActionType.Add);
            Snapshots.Add(CurrentSnapshot);
        }


        /// <summary>
        /// 
        /// </summary>
        [Required]
        public virtual string Name
        {
            get { return CurrentSnapshot.Name; }
            set { CurrentSnapshot.Name = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        public virtual string Memo
        {
            get { return CurrentSnapshot.Memo; }
            set { CurrentSnapshot.Memo = value; }
        }

        /// <summary>
        /// 
        /// </summary>
        public virtual ISnapShot CurrentSnapshot
        {
            get
            {
                if (_currentSnapshot == null)
                {
                    if (Snapshots.Count != 0)
                    {
                        _currentSnapshot = Snapshots[0];
                    }
                }
                return _currentSnapshot;
            }
            private set { _currentSnapshot = value; }
        }

        protected virtual IList<ISnapShot> Snapshots
        {
            get { return _snapshots ?? (_snapshots = new List<ISnapShot>()); }
        }

        public virtual DisplayState DisplayState
        {
            get
            {
                if (_switchProductVersion == null)
                    _switchProductVersion = CurrentSnapshot.Version;
                if (CurrentSnapshot.Version > SwitchedVersion)
                {
                    return DisplayState.FetureDefined;
                }
                if (CurrentSnapshot.State == ActionType.Delete)
                {
                    return DisplayState.Delete;
                }
                if (CurrentSnapshot.Version < SwitchedVersion)
                    return (DisplayState.PastDefined | DisplayState.Normal);

                return DisplayState.Current | DisplayState.Normal;

            }
        }
        /// <summary>
        /// Gets the target version from <see cref="SwitchTo"/> 
        /// </summary>
        public virtual ProductVersion SwitchedVersion
        {
            get { return this._switchProductVersion; }
        }
        public virtual ProductVersion CreateVersion
        {
            get { return _snapshots[_snapshots.Count - 1].Version; }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="productVersion"></param>
        /// <returns></returns>
        public virtual bool SwitchTo(ProductVersion productVersion)
        {
            _switchProductVersion = productVersion;
            ISnapShot result = (from snapShot in Snapshots
                                where snapShot.Version.OrderValue <= productVersion.OrderValue
                                orderby snapShot.Version.OrderValue descending
                                select
                                    snapShot).FirstOrDefault();
            if (result != null)
            {
                CurrentSnapshot = result;
                return true;
            }
            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="productVersion"></param>
        /// <param name="delete"></param>
        public virtual ISnapShot NewChange(ProductVersion productVersion, bool delete)
        {
            if (productVersion == null)
            {
                throw new ArgumentNullException("productVersion");
            }
            var theVersion = from snapShot in Snapshots
                             where snapShot.Version.Equals(productVersion)
                             select snapShot;
            if (theVersion.Count() > 0)
            {
                throw new SnapshotException("ProductVersion state has defined, can't create it again.");
            }

            if (CurrentSnapshot.Id != Guid.Empty)
            {
                ISnapShot lastVersion = null;
                if (delete)
                {
                    lastVersion = this.Snapshots[0];
                    if (lastVersion.Version >= productVersion)
                    {
                        throw new ArgumentException("Delete action can't apply to previous ProductVersion.");
                    }
                }
                CurrentSnapshot = CreateNewStateChange(productVersion, BuildActionType(delete));
                if (delete)
                {
                    FillDeleteVersion(CurrentSnapshot, lastVersion);
                }

                Snapshots.Add(CurrentSnapshot);
            }
            return CurrentSnapshot;
        }

        protected virtual ActionType BuildActionType(bool delete)
        {
            if (delete)
                return ActionType.Delete;
            return Id == Guid.Empty ? ActionType.Add : ActionType.Update;
        }


        protected abstract ISnapShot CreateNewStateChange(ProductVersion productVersion, ActionType actionType);

        protected override int CreateHashCode()
        {
            return Id.GetHashCode();
        }

        protected virtual void FillDeleteVersion(ISnapShot deletedSnap, ISnapShot lastSnapshot)
        {
            deletedSnap.Memo = lastSnapshot.Memo;
            deletedSnap.Name = lastSnapshot.Name;
        }
    }
}